package in.omerjerk.droidcv; import android.content.Context; import android.hardware.display.DisplayManager; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.net.Uri; import android.text.TextUtils; import android.util.Log; import android.view.Surface; import org.xml.sax.Attributes; import safesax.Element; import safesax.ElementListener; import safesax.Parsers; import safesax.RootElement; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; /** * Created by omerjerk on 12/7/14. */ public class CoreWorker { private static final String TAG = "AndroidDisplayView"; private static final boolean DEBUG = true; private Context mContext = null; MediaCodec encoder = null; MediaCodec decoder = null; DisplayFrameListener mListener = null; public CoreWorker(Context context, DisplayFrameListener listener) { this.mContext = context; this.mListener = listener; } public void startRendering(int mode, Uri videoSource) throws IllegalArgumentException{ CodecWorker mWorker = null; switch (mode) { case CoreDisplayService.MODE_DISPLAY_SCREEN: DisplayManager mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); Surface encoderInputSurface = createDisplaySurface(); mDisplayManager.createVirtualDisplay("OpenCV Virtual Display", 960, 1280, 150, encoderInputSurface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE); mWorker = new CodecWorker(true); break; case CoreDisplayService.MODE_VIDEO: if (videoSource == null) { throw new IllegalArgumentException("You must override the setVideoSource method in your service."); } mWorker = new CodecWorker(false, videoSource); //TODO: write the rest of the code. break; default: throw new UnsupportedOperationException(); } if (mWorker != null) { Thread encoderThread = new Thread(mWorker); encoderThread.start(); } } private Surface createDisplaySurface() { int bitrate; int maxFrameRate; int height = 1280; int width = 960; if (encoder != null) { try { encoder.signalEndOfInputStream(); } catch (Exception e) { } encoder = null; } try { String xml = StreamUtility.readFile("/system/etc/media_profiles.xml"); RootElement root = new RootElement("MediaSettings"); Element encoderElement = root.requireChild("VideoEncoderCap"); ArrayList<VideoEncoderCap> encoders = new ArrayList(); XmlListener mXmlListener = new XmlListener(encoders); encoderElement.setElementListener(mXmlListener); Reader mReader = new StringReader(xml); Parsers.parse(mReader, root.getContentHandler()); if (encoders.size() != 1) { throw new Exception("derp"); } else { VideoEncoderCap v = encoders.get(0); int maxWidth = v.maxFrameWidth; int maxHeight = v.maxFrameHeight; bitrate = v.maxBitRate; maxFrameRate = v.maxFrameRate; MediaFormat mMediaFormat = MediaFormat.createVideoFormat("video/avc", width, height); mMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); mMediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15); mMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); mMediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10); Log.i(TAG, "Starting encoder"); encoder = MediaCodec.createByCodecName(CodecUtils.selectCodec(CodecUtils.MIME_TYPE).getName()); encoder.configure(mMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface surface = encoder.createInputSurface(); encoder.start(); return surface; } } catch (Exception e) { e.printStackTrace(); } return null; } private class CodecWorker implements Runnable { private boolean isFromSurface = false; private Uri videoSource = null; public CodecWorker(boolean isFromSurface) { this(isFromSurface, null); } public CodecWorker(boolean isFromSurface, Uri videoSource) { this.isFromSurface = isFromSurface; this.videoSource = videoSource; } @Override public void run() { DisplayFrame displayFrame = new DisplayFrame(1024, 1280); mListener.onDisplayFrameStarted(); if (isFromSurface) { decoder = MediaCodec.createDecoderByType(CodecUtils.MIME_TYPE); CodecUtils.doEncodeDecodeVideoFromSurface(encoder, decoder, mListener, displayFrame); } else { try { CodecUtils.doDecodeFromVideo(videoSource, mListener, displayFrame); } catch (IOException e) { e.printStackTrace(); } } mListener.onDisplayFrameStopped(); } } class XmlListener implements ElementListener { final ArrayList<VideoEncoderCap> encoders; XmlListener(ArrayList mList) { this.encoders = mList; } @Override public void end() { } @Override public void start(Attributes attributes) { if (TextUtils.equals(attributes.getValue("name"), "h264")) { this.encoders.add(new VideoEncoderCap(attributes)); } } } private static class VideoEncoderCap { int maxBitRate; int maxFrameHeight; int maxFrameRate; int maxFrameWidth; public VideoEncoderCap(Attributes attributes) { this.maxFrameWidth = Integer.valueOf(attributes.getValue("maxFrameWidth")).intValue(); this.maxFrameHeight = Integer.valueOf(attributes.getValue("maxFrameHeight")).intValue(); this.maxBitRate = Integer.valueOf(attributes.getValue("maxBitRate")).intValue(); this.maxFrameRate = Integer.valueOf(attributes.getValue("maxFrameRate")).intValue(); } } }